package com.wanjia.estate.utils;

import com.wanjia.estate.exception.LinuxPrintException;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

@Slf4j
public class LinuxPrintUtil {

    private static Pattern errorPattern = Pattern.compile("^.*Error: (?<errorMessage>.*)$");

    private static Pattern successPattern = Pattern.compile("^request id is .*-(?<requestId>\\d+) \\(\\d+ file\\(s\\)\\)$");

    public static void linuxPrint(String printprefix, String pdfpath, String printername, String printsuffix) {
        // 命令拆分解析
        List<String> commands = new ArrayList<>();
        Stream.of(printprefix, pdfpath, printername, printsuffix)
                .filter(Objects::nonNull)
                .map(s -> s.split("\\s"))
                .flatMap(Stream::of)
                .forEach(commands::add);


        log.info("commands{}",commands);
        // 执行并获取id
        BufferedReader reader = null;
        try {
            Process process = new ProcessBuilder()
                    .command(commands)
                    .redirectErrorStream(true)
                    .start();

            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            String id = null;
            while ((line = reader.readLine()) != null) {
                log.debug("打印程序输出: {}", line);

                // 异常判断解析
                Matcher errorMatcher = errorPattern.matcher(line);
                if (errorMatcher.matches()) {
                    String errorMessage = errorMatcher.group("errorMessage");
                    throw new LinuxPrintException(errorMessage);
                }

                // 请求id获取
                Matcher successMatcher = successPattern.matcher(line);
                if (successMatcher.matches()) {
                    id = successMatcher.group("requestId");
                    break;
                }
            }

            if (id == null) {
                throw new LinuxPrintException("打印机调用结果解析异常");
            }

            // 循环查询系统打印队列直到打印完成
            if (!checkId(id)) {
                throw new LinuxPrintException("打印机调用失败");
            }
        } catch (IOException e) {
            throw new LinuxPrintException("打印机调用异常", e);
        } catch (InterruptedException e) {
            throw new LinuxPrintException("打印机调用结果查询意外中断", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.warn("输入流关闭异常", e);
                }
            }
        }

    }

    private static boolean checkId(final String id) throws InterruptedException {
        AtomicBoolean success = new AtomicBoolean(false);
        new Thread(() -> {
            synchronized (LinuxPrintUtil.class) {
                for (int i = 0; i < 10; i++) {

                    Map<String, Boolean> jobMap = getJobMap();
                    if (!jobMap.containsKey(id)) {
                        // 队列消失时，打印完成
                        success.set(true);
                        break;
                    } else if (!jobMap.get(id)) {
                        // 队列存在，状态非打印中，则判定为异常
                        break;
                    }

                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        throw new RuntimeException("睡眠意外中断", e);
                    }
                }
                LinuxPrintUtil.class.notify();
            }
        }).start();

        synchronized (LinuxPrintUtil.class) {
            LinuxPrintUtil.class.wait();
        }
        return success.get();
    }

    private static Pattern headerPattern = Pattern.compile("Rank\\s+Owner\\s+Job\\s+File\\(s\\)\\s+Total Size");

    private static Pattern noEntriesPattern = Pattern.compile("(no\\s+entries|无条目)");

    private static Pattern jobPattern = Pattern.compile("(?<Rank>\\S+)\\s+(?<Owner>\\S+)\\s+(?<Job>\\d+)\\s+(?<File>\\S+)\\s+(?<TotalSize>\\d+ .*)");

    private static Map<String, Boolean> getJobMap() {
        Map<String, Boolean> map = new HashMap<>();
        BufferedReader reader = null;
        try {
            Process process = new ProcessBuilder()
                    .command("lpq", "-a")
                    .redirectErrorStream(true)
                    .start();

            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            boolean hasNext = true;
            String line = reader.readLine();

            // 空队列判断
            Matcher noEntriesMatcher = noEntriesPattern.matcher(line);
            if (noEntriesMatcher.matches()) {
                return map;
            }

            // 队列表头判断
            Matcher headerMatcher = headerPattern.matcher(line);
            if (!headerMatcher.matches()) {
                throw new LinuxPrintException("未知的队列查询表头: " + line);
            }

            while ((line = reader.readLine()) != null) {
                log.debug("打印程序输出: {}", line);
                Matcher matcher = jobPattern.matcher(line);
                if (matcher.matches()) {
                    String job = matcher.group("Job");
                    String rank = matcher.group("Rank");
                    map.put(job, Objects.equals(rank, "active"));
                } else {
                    log.warn("未知的队列格式: {}", line);
                }
            }

        } catch (IOException e) {
            throw new LinuxPrintException("打印机调用异常", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.warn("输入流关闭异常", e);
                }
            }
        }

        return map;
    }
}
